/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
 * MA  02110-1301, USA.
 *
 * http://www.gnu.org/copyleft/gpl.html
 */
package com.aionemu.gameserver.ai;

import com.aionemu.gameserver.model.Position;
import com.aionemu.gameserver.model.gameobjects.Creature;
import com.aionemu.gameserver.model.gameobjects.VisibleObject;
import com.aionemu.gameserver.model.templates.npcTemplate.CreatureTemplate;
import com.aionemu.gameserver.skillengine.model.SkillTemplate;
import com.aionemu.gameserver.taskmanager.AbstractIterativePeriodicTaskManager;
import com.aionemu.gameserver.taskmanager.ExclusiveTask;
import com.aionemu.gameserver.utils.MathUtil;

/**
 * @author Mr. Poke
 *
 */
public class CreatureAI extends AbstractAI implements Runnable
{

	/**
	 * @param owner
	 */
	public CreatureAI(Creature owner)
	{
		super(owner);
	}

	private static final class CreatureAiTaskManager extends AbstractIterativePeriodicTaskManager<CreatureAI>
	{
		private static final CreatureAiTaskManager	_instance	= new CreatureAiTaskManager();

		static CreatureAiTaskManager getInstance()
		{
			return _instance;
		}

		private CreatureAiTaskManager()
		{
			super(1000);
		}

		@Override
		protected void callTask(CreatureAI task)
		{
			task.run();
		}

		@Override
		protected String getCalledMethodName()
		{
			return "run()";
		}
	}

	public void startAITask()
	{
		if (CreatureAiTaskManager.getInstance().hasTask(this))
			return;
		CreatureAiTaskManager.getInstance().startTask(this);
	}

	public void stopAITask()
	{
		CreatureAiTaskManager.getInstance().stopTask(this);
	}

	@Override
	public void stopAI()
	{
		super.stopAI();
		this.stopAITask();
	}

	@Override
	public void startAI()
	{
		startAITask();
	}

	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */

	@Override
	public void run()
	{
		// Launch actions corresponding to the Event Think
		notifyEvent(CtrlEvent.EVT_THINK);
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#getOwner()
	 */
	@Override
	public Creature getOwner()
	{
		return (Creature)super.getOwner();
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onIntentionIdle()
	 */
	@Override
	protected void onIntentionIdle()
	{
		// Set the AI Intention to AI_INTENTION_IDLE
		changeIntention(CtrlIntention.IDLE, null, null);
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onIntentionActive()
	 */
	@Override
	protected void onIntentionActive()
	{
		if (getIntention() != CtrlIntention.ACTIVE)
		{
			// Set the AI Intention to AI_INTENTION_ACTIVE
			changeIntention(CtrlIntention.ACTIVE, null, null);
			// Launch the Think Event
			notifyEvent(CtrlEvent.EVT_THINK);
		}
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onIntentionRest()
	 */
	@Override
	protected void onIntentionRest()
	{
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onIntentionAttack(com.aionemu.gameserver.model.gameobjects.Creature)
	 */
	@Override
	protected void onIntentionAttack(Creature target)
	{

		if (target == null)
		{
			return;
		}

		if (getIntention() == CtrlIntention.REST)
		{
			return;
		}

		// Check if the Intention is already AI_INTENTION_ATTACK
		if (getIntention() == CtrlIntention.ATTACK && getAttackTarget() == target)
		{
			return;
		}
		
		// Set the Intention of this AbstractAI to AI_INTENTION_ATTACK
		changeIntention(CtrlIntention.ATTACK, target, null);
		
		stopFollow();
		
		// Launch the Think Event
		notifyEvent(CtrlEvent.EVT_THINK, null);
	
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onIntentionCast(com.aionemu.gameserver.skillengine.model.SkillTemplate)
	 */
	@Override
	protected void onIntentionCast(SkillTemplate request)
	{
		changeIntention(CtrlIntention.CAST, request, target);
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onIntentionMoveTo(com.aionemu.gameserver.model.Position)
	 */
	@Override
	protected void onIntentionMoveTo(Position destination)
	{
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onIntentionFollow(com.aionemu.gameserver.model.gameobjects.Creature)
	 */
	@Override
	protected void onIntentionFollow(Creature target)
	{
		// Set the Intention of this AbstractAI to AI_INTENTION_FOLLOW
		changeIntention(CtrlIntention.FOLLOW, target, null);

		// Create and Launch an AI Follow Task to execute every 1s
		startFollow(target);
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onEvtThink()
	 */
	@Override
	protected void onEvtThink()
	{
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onEvtAttacked(com.aionemu.gameserver.model.gameobjects.Creature)
	 */
	@Override
	protected void onEvtAttacked(Creature attacker)
	{
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onEvtAggression(com.aionemu.gameserver.model.gameobjects.Creature, int)
	 */
	@Override
	protected void onEvtAggression(Creature target, int aggro)
	{
	}


	/**
	 * Launch actions corresponding to the Event ReadyToAct.<BR><BR>
	 *
	 * <B><U> Actions</U> :</B><BR><BR>
	 * <li>Launch actions corresponding to the Event Think</li><BR><BR>
	 *
	 */
	@Override
	protected void onEvtReadyToAct()
	{
		executeNextIntention();
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onEvtArrived()
	 */
	@Override
	protected void onEvtArrived()
	{
		notifyEvent(CtrlEvent.EVT_THINK);
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onEvtArrivedRevalidate()
	 */
	@Override
	protected void onEvtArrivedRevalidate()
	{
		followTarget();
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onEvtForgetObject(com.aionemu.gameserver.model.gameobjects.VisibleObject)
	 */
	@Override
	protected void onEvtForgetObject(VisibleObject object)
	{
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onEvtCancel()
	 */
	@Override
	protected void onEvtCancel()
	{
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onEvtDead()
	 */
	@Override
	protected void onEvtDead()
	{
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.ai.AbstractAI#onEvtFinishCasting()
	 */
	@Override
	protected void onEvtFinishCasting()
	{
	}

	protected boolean moveToPawn(VisibleObject pawn, float offset)
	{
		if(getOwner().canPerformMove() && getOwner().getGameStats().getMovementSpeed().getCurrent()/1000f > 0)
		{
			float ownerX = owner.getX();
			float ownerY = owner.getY();
			float ownerZ = owner.getZ();
			
			float targetX = pawn.getX();
			float targetY = pawn.getY();
			float targetZ = pawn.getZ();

			double dist = MathUtil.getDistance(ownerX, ownerY, ownerZ, targetX, targetY, targetZ);

			float distX = (float)((targetX-ownerX)/dist*(dist-offset))+ownerX;
			float distY = (float)((targetY-ownerY)/dist*(dist-offset))+ownerY;
			float distZ = (float)((targetZ-ownerZ)/dist*(dist-offset))+ownerZ;

			
			if (dist < offset)
			{
				notifyEvent(CtrlEvent.EVT_ARRIVED);
				return false;
			}
			getOwner().onMoveTo(distX, distY, distZ);
			return true;
		}
		return false;
	}
	
	protected boolean attackTarget()
	{
		if (getAttackTarget() == null)
			return false;
			
		int attackDelay = (int)getOwner().getGameStats().getAttackSpeed().getCurrent();
		
		if (getOwner().attackTarget(getAttackTarget(), attackDelay))
		{
			int attackTime = ((CreatureTemplate)getOwner().getObjectTemplate()).getCreatureStatData().getAttackRate();
			
			if (attackTime > 0)
			{
				getAttackEndEvtReadyToAct().schedule(attackTime);
			}
			if (attackDelay > 0)
			{
				getAttackReuseEndEvtReadyToAct().schedule(attackDelay);
			}
			return true;
		}
		return false;
	}
	
	private EvtReadyToAct attackEndEvtReadyToAct;
	private EvtReadyToAct attackReuseEndEvtReadyToAct;
	
	protected EvtReadyToAct getAttackEndEvtReadyToAct()
	{
		if (attackEndEvtReadyToAct == null)
			attackEndEvtReadyToAct = new EvtReadyToAct();
		
		return attackEndEvtReadyToAct;
	}
	
	protected EvtReadyToAct getAttackReuseEndEvtReadyToAct()
	{
		if (attackReuseEndEvtReadyToAct == null)
			attackReuseEndEvtReadyToAct = new EvtReadyToAct();
		
		return attackReuseEndEvtReadyToAct;
	}

	public final class EvtReadyToAct extends ExclusiveTask
	{
		@Override
		protected void onElapsed()
		{
			cancel();
			
			notifyEvent(CtrlEvent.EVT_READY_TO_ACT);
		}
	}
}
